﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Transactions;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity;
using Microsoft.Practices.Unity;
using Vallejos.Datos.Factory;
using Vallejos.Datos.Servicios;
using Vallejos.Entidades.Interfaces;
using Vallejos.Entidades.ModeloDeDatos;
using Vallejos.Excepciones;

namespace Vallejos.Datos.Accesos
{
    public class ProductoDAL : IProducto
    {
        private readonly DatabaseService _database;

        public ProductoDAL()
        {
            var container = new UnityContainer()
                .AddNewExtension<EnterpriseLibraryCoreExtension>();
            _database = container.Resolve<DatabaseService>();
        }

        public List<Marca> LeerMarcas()
        {
            var marcas = new List<Marca>();

            var factory = new MarcaFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerMarcas"))
                {
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            marcas.Add(factory.Crear(reader));
                    
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return marcas;
        }

        public List<Modelo> LeerModelos()
        {
            var modelos = new List<Modelo>();

            var factory = new ModeloFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerModelos"))
                {
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            modelos.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return modelos;
        }


        public List<TipoCombustible> LeerTiposCombustible(int idLenguaje)
        {
            var tipos = new List<TipoCombustible>();

            var factory = new TipoCombustibleFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerTiposCombustible"))
                {
                    _database.AddInParameter(command, "Id_Lenguaje", DbType.Int32, idLenguaje);
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            tipos.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return tipos;
        }

        public List<TipoFinanciacion> LeerTiposFinanciacion(int idLenguaje)
        {
            var tipos = new List<TipoFinanciacion>();

            var factory = new TipoFinanciacionFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerTiposFinanciacion"))
                {
                    _database.AddInParameter(command, "Id_Lenguaje", DbType.Int32, idLenguaje);
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            tipos.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return tipos;
        }

        public List<UsoDeProducto> LeerUsosDeProducto(int idLenguaje)
        {
            var usos = new List<UsoDeProducto>();

            var factory = new UsoDeProductoFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerUsosDeProducto"))
                {
                    _database.AddInParameter(command, "Id_Lenguaje", DbType.Int32, idLenguaje);
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            usos.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return usos;
        }

        public List<TipoProducto> LeerTiposProducto(int idLenguaje)
        {
            var tipos = new List<TipoProducto>();

            var factory = new TipoProductoFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerTiposProducto"))
                {
                    _database.AddInParameter(command, "Id_Lenguaje", DbType.Int32, idLenguaje);
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            tipos.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return tipos;
        }


        public Producto LeerProducto(int idProducto)
        {
            Producto producto = null;

            var factory = new ProductoFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerProducto"))
                {
                    _database.AddInParameter(command, "Id_Producto", DbType.Int32, idProducto);

                    using (var reader = _database.ExecuteReader(command))
                        if (reader.Read())
                        {
                            producto = factory.Crear(reader);
                            producto.Prestaciones = LeerPrestacionesDeProducto(producto.Id);
                            producto.Imagenes = LeerImagenesDeProducto(producto.Id);
                            producto.Consultas = LeerConsultasDeProducto(producto.Id);
                        }
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return producto;
        }

        
        public Producto GuardarProducto(Producto producto)
        {
            try
            {
                using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    using (var command = _database.GetStoredProcCommand("GuardarProducto"))
                    {

                        if (producto.Id != 0)
                            _database.AddInParameter(command, "Id", DbType.Int32, producto.Id);
                        else
                            _database.AddOutParameter(command, "Id", DbType.Int32, Int32.MaxValue);

                        _database.AddInParameter(command, "Titulo", DbType.String, producto.Titulo);
                        _database.AddInParameter(command, "Descripcion", DbType.String, producto.Descripcion);
                        _database.AddInParameter(command, "Anio", DbType.Int32, producto.Anio);
                        _database.AddInParameter(command, "Activo", DbType.Boolean, producto.Activo);
                        _database.AddInParameter(command, "Id_TipoFinanciacion", DbType.Int32, (int)producto.TipoDeFinanciacion.idTipo);
                        _database.AddInParameter(command, "Id_Marca", DbType.Int32, producto.Marca.Id);
                        _database.AddInParameter(command, "Id_Modelo", DbType.Int32, producto.Modelo.Id);
                        _database.AddInParameter(command, "Id_TipoProducto", DbType.Int32, (int)producto.TipoDeProducto.idTipo);
                        _database.AddInParameter(command, "Id_TipoCombustible", DbType.Int32, (int)producto.TipoDeCombustible.idTipo);
                        _database.AddInParameter(command, "Id_Uso", DbType.Int32, (int)producto.Uso.idUso);
                        _database.AddInParameter(command, "Id_Concesionaria", DbType.Int32, producto.Concesionaria.Id);
                        _database.AddInParameter(command, "Precio", DbType.Decimal, producto.Precio);
                        _database.AddInParameter(command, "GastosAdministrativos", DbType.Decimal, producto.GastosAdministrativos);
                        _database.AddInParameter(command, "GastosDeGestoria", DbType.Decimal, producto.GastosDeGestoria);
                        _database.AddInParameter(command, "SobreescribeImagenes", DbType.Boolean, producto.Imagenes.Count > 0);

                        _database.ExecuteNonQuery(command);

                        if (producto.Id == 0)
                            producto.Id = Convert.ToInt32(_database.GetParameterValue(command, "Id"));

                        if (producto.Prestaciones.Count > 0)
                            AsociarPrestacionesAlProducto(producto);

                        if (producto.Imagenes.Count > 0)
                            AsociarImagenesAlProducto(producto);

                        scope.Complete();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return producto;
        }

        private void AsociarPrestacionesAlProducto(Producto producto)
        {
            try
            {
                
                using (var command = _database.GetStoredProcCommand("AsociarPrestacionesAlProducto"))
                {
                    _database.AddInParameter(command, "Id_Producto", DbType.Int32, producto.Id);
                    _database.AddInParameter(command, "Descripcion", DbType.String, 0);

                    foreach (var prestacion in producto.Prestaciones)
                    {
                        _database.SetParameterValue(command, "Descripcion", prestacion.Descripcion);
                        _database.ExecuteNonQuery(command);
                    }

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }
        }

        private void AsociarImagenesAlProducto(Producto producto)
        {
            try
            {
                using (var command = _database.GetStoredProcCommand("AsociarImagenAlProducto"))
                {
                    _database.AddInParameter(command, "NombreDeArchivo", DbType.String, String.Empty);
                    _database.AddInParameter(command, "Contenido", DbType.String, String.Empty);
                    _database.AddInParameter(command, "Tamanio", DbType.Int32, String.Empty);
                    _database.AddInParameter(command, "Stream", DbType.Binary, String.Empty);
                    _database.AddInParameter(command, "Id_Producto", DbType.Int32, producto.Id);
                        

                    foreach (var imagen in producto.Imagenes)
                    {
                        _database.SetParameterValue(command, "NombreDeArchivo", imagen.NombreDeArchivo);
                        _database.SetParameterValue(command, "Contenido", imagen.Contenido);
                        _database.SetParameterValue(command, "Tamanio", imagen.Tamanio);
                        _database.SetParameterValue(command, "Stream", imagen.Stream);
                        _database.ExecuteNonQuery(command);
                    }

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }
        }

        private List<Prestacion> LeerPrestacionesDeProducto(int idProducto)
        {
            var prestaciones = new List<Prestacion>();

            var factory = new PrestacionFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerPrestacionesDeProducto"))
                {
                    _database.AddInParameter(command, "Id_Producto", DbType.Int32, idProducto);
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            prestaciones.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return prestaciones;
        }

        private List<Imagen> LeerImagenesDeProducto(int idProducto)
        {
            var imagenes = new List<Imagen>();

            var factory = new ImagenFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerImagenesDeProducto"))
                {
                    _database.AddInParameter(command, "Id_Producto", DbType.Int32, idProducto);
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            imagenes.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return imagenes;
        }


        public List<Producto> BuscarProductos(Producto producto, int idLenguaje, int desde, int hasta)
        {
            return null;
        }

        public List<Producto> BuscarProductos(Producto producto, int desde, int hasta)
        {
            var productos = new List<Producto>();

            var factory = new ProductoFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("BuscarProductos"))
                {
                    if (producto.Concesionaria != null)
                        _database.AddInParameter(command, "Id_Concesionaria", DbType.Int32, producto.Concesionaria.Id);
                    _database.AddInParameter(command, "Titulo", DbType.String, producto.Titulo);
                    if (producto.TipoDeProducto != null)
                        _database.AddInParameter(command, "Id_TipoProducto", DbType.Int32, (int)producto.TipoDeProducto.idTipo);
                    if (producto.Uso != null)
                        _database.AddInParameter(command, "Id_Uso", DbType.Int32, (int)producto.Uso.idUso);
                    if (producto.Marca != null)
                        _database.AddInParameter(command, "Id_Marca", DbType.Int32, producto.Marca.Id);
                    if (producto.Modelo != null)
                        _database.AddInParameter(command, "Id_Modelo", DbType.Int32, producto.Modelo.Id);
                    _database.AddInParameter(command, "Anio_Desde", DbType.Int32, desde);
                    _database.AddInParameter(command, "Anio_Hasta", DbType.Int32, hasta);

                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                        {
                            var prod = factory.Crear(reader);
                            prod.Imagenes = LeerImagenesDeProducto(prod.Id);
                            productos.Add(prod);
                        }
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return productos;
        }


        public void RealizarConsulta(Consulta consulta)
        {
            try
            {
                using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    using (var command = _database.GetStoredProcCommand("RealizarConsulta"))
                    {
                        _database.AddInParameter(command, "Pregunta", DbType.String, consulta.Pregunta);
                        _database.AddInParameter(command, "Id_Cliente", DbType.Int32, consulta.Cliente.Id);
                        _database.AddInParameter(command, "Id_Producto", DbType.Int32, consulta.Producto.Id);
                        _database.AddInParameter(command, "EsPublica", DbType.Boolean, consulta.EsPublica);

                        
                        _database.ExecuteNonQuery(command);
                        scope.Complete();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }
        }

        public List<Consulta> LeerConsultasDeProducto(int idProducto)
        {
            var consultas = new List<Consulta>();

            var factory = new ConsultaFactory();

            try
            {
                using (var command = _database.GetStoredProcCommand("LeerConsultasDeProducto"))
                {
                    _database.AddInParameter(command, "Id_Producto", DbType.Int32, idProducto);
                    using (var reader = _database.ExecuteReader(command))
                        while (reader.Read())
                            consultas.Add(factory.Crear(reader));

                }
            }
            catch (Exception ex)
            {
                throw new ExcepcionDatos("Error desconocido", ex);
            }

            return consultas;
        }
    }
}